import numpy as np
import math


def sigmoid(x):
    return 1.0 / (1.0 + math.exp(-x))


def sigmoid_d(x):
    return x * (1 - x)


class NN:
    def __init__(self, num_in, num_hide, num_out):
        self.num_in = num_in
        self.num_hide = num_hide
        self.num_out = num_out

        self.neurons_in = [0] * self.num_in
        self.neurons_hide = [0] * self.num_hide
        self.neurons_out = [0] * self.num_out

        self.w_in = np.random.normal(0, 1, [self.num_in, self.num_hide]).tolist()
        self.w_hide = np.random.normal(0, 1, [self.num_hide, self.num_out]).tolist()
        # self.d_hide = np.random.normal(0, 1, [self.num_hide]).tolist()
        # self.d_out = np.random.normal(0, 1, [self.num_out]).tolist()

    def fp(self, features):
        # 取得输入层神经元值
        for i in range(self.num_in):
            self.neurons_in[i] = features[i]

        # 计算隐藏层神经元值
        for j in range(self.num_hide):
            sum = 0
            for i in range(self.num_in):
                sum += self.neurons_in[i] * self.w_in[i][j]
            self.neurons_hide[j] = sigmoid(sum)
            # self.neurons_hide[j] = sigmoid(sum + self.d_hide[j])

        # 计算输出层神经元值
        for k in range(self.num_out):
            sum = 0
            for j in range(self.num_hide):
                sum += self.neurons_hide[j] * self.w_hide[j][k]
            self.neurons_out[k] = sigmoid(sum)
            # self.neurons_out[k] = sigmoid(sum + self.d_out[k])

        return self.neurons_out

    def bp(self, labels, lr):
        deviation_out = [0] * self.num_out
        for k in range(self.num_out):
            deviation = labels[k] - self.neurons_out[k]
            deviation_out[k] = deviation * sigmoid_d(self.neurons_out[k])

        deviation_hide = [0] * self.num_hide
        for j in range(self.num_hide):
            deviation = 0
            for k in range(self.num_out):
                deviation += deviation_out[k] * self.w_hide[j][k]
            deviation_hide[j] = deviation * sigmoid_d(self.neurons_hide[j])

        for j in range(num_hide):
            for k in range(num_out):
                self.w_hide[j][k] += lr * deviation_out[k] * self.neurons_hide[j]

        for i in range(self.num_in):
            for j in range(self.num_hide):
                self.w_in[i][j] += lr * deviation_hide[j] * self.neurons_in[i]

        return 0.5 * (labels[k] - self.neurons_out[k]) ** 2


# 读入数据集
train_path = "./Iris/Iris-train.txt"
test_path = "./Iris/Iris-test.txt"

features = []
labels = []
features_train = []
labels_train = []
features_test = []
labels_test = []

with open(train_path, 'r') as f_train:
    for line in f_train:
        line = line.split()
        features_train.append(list(map(float, line[:4])))
        labels_train.append(list(map(int, line[-1]))[0])

with open(test_path, 'r') as f_test:
    for line in f_test:
        line = line.split()
        features_test.append(list(map(float, line[:4])))
        labels_test.append(list(map(int, line[-1]))[0])

num_in = 4
num_hide = 10
num_out = 3
nn = NN(num_in, num_hide, num_out)

for epoc in range(500):
    for i in range(len(features_train)):
        nn.fp(features_train[i])
        deviation = nn.bp(labels_train, lr=0.1)
    print('误差：', deviation)

count = 0
for j in range(len(features_test)):
    result = nn.fp(features_test[j])
    category = result.index(max(result))
    print('输入：', features_test[j])
    print('预测结果：', category)
    print('实际结果：', labels_test[j])
    print('预测正确！' if category == labels_test[j] else '预测错误！')
    print('--------------------')
    count += (category == labels_test[j])
accuracy = count / len(features_test)
print('准确率：', accuracy)
